#pragma once

#include <iostream>
#include <ostream>
#include <string>

#include "frontend/location.h"

#define COLOR(c) "\e[38;5;" #c "m"
#define DEF "\e[0m"
#define BOLD "\e[1m"
#define GREY COLOR(250)
#define RED COLOR(1)
#define BLUE COLOR(4)
#define GREEN COLOR(2)

namespace Utils {
    std::string Color(int c);

    std::string Format(const char* format);

    template <class... Args>
    std::string Format(const char* format, Args&&... args) {
        char buf[128];
        snprintf(buf, sizeof(buf), format, args...);
        return buf;
    }

    template <class... Args>
    void Err(const char* msg, Args&&... args) {
        std::cerr << RED;
        std::cerr << Format(msg, std::forward<Args>(args)...) << std::endl;
        std::cerr << DEF << std::endl;
        throw nullptr;
    }

    template <class... Args>
    void ErrAt(Location loc, const char* msg, Args&&... args) {
        std::cerr << DEF << "At " << loc << ":\n";
        Err(msg, std::forward<Args>(args)...);
        throw nullptr;
    }

    template <class... Args>
    void Assert(bool cond, const char* msg, Args&&... args) {
        if (cond) return;
        Err(msg, std::forward<Args>(args)...);
    }

    template <class... Args>
    void AssertAt(Location loc, bool cond, const char* msg, Args&&... args) {
        if (cond) return;
        ErrAt(loc, msg, std::forward<Args>(args)...);
    }

    void Indent(int n);

    void Splitter(int n = 60);

}  // namespace Utils
